/*
 * load_pcap.c
 *
 *  Created on: 2012-2-27
 *      Author: lius
 */

#include "load_pcap.h"

static char *tcp_data[1500];
static int tcp_data_len;
static char tcp_side;
static char tcp_state;

//pcap file header "0xd4 0xc3 0xb2 0xa1"
static int is_pcap_file(char *file)
{
	FILE *fp = fopen(file, "rb");
	if(fp == NULL)
	{
		return 0;
	}
	char buff[4] = {};
	int res = fread(buff, 1, sizeof(buff), fp);
	fclose(fp);
	if(res < 4)
	{
		return 0;
	}

	char high = 0xa0;
	char low  = 0x01;
	char high_step = 0x10;

	for(res = res --; res >= 0; res--, low++, high += high_step)
	{
		if(buff[res] != high + low)
			break;
	}

	if(res < 0)
		return 1;
	return 0;
}

#define int_ntoa(x)     inet_ntoa(*((struct in_addr *)&x))
static char *adres (struct tuple4 addr)
{
  static char buf[256];
  strcpy (buf, int_ntoa (addr.saddr));
  sprintf (buf + strlen (buf), ",%i,", addr.source);
  strcat (buf, int_ntoa (addr.daddr));
  sprintf (buf + strlen (buf), ",%i", addr.dest);
  return buf;
}

static void tcp_callback (struct tcp_stream *a_tcp, void ** this_time_not_needed)
{
	char buf[1024];
	strcpy(buf, adres(a_tcp->addr)); // we put conn params into buf
	if (a_tcp->nids_state == NIDS_JUST_EST)
	{
		tcp_state = NIDS_JUST_EST;
		// connection described by a_tcp is established
		// here we decide, if we wish to follow this stream
		// sample condition: if (a_tcp->addr.dest!=23) return;
		// in this simple app we follow each stream, so..
		a_tcp->client.collect++; // we want data received by a client
		a_tcp->server.collect++; // and by a server, too
		//a_tcp->server.collect_urg++; // we want urgent data received by a
		// server
		//fprintf(stderr, "%s established\n", buf);
		return;
	}

	if (a_tcp->nids_state == NIDS_CLOSE)
	{
		tcp_state = NIDS_CLOSE;
		// connection has been closed normally
		//fprintf(stderr, "%s closing\n", buf);
		return;
	}

	if (a_tcp->nids_state == NIDS_RESET)
	{
		tcp_state = NIDS_RESET;
		// connection has been closed by RST
		//fprintf(stderr, "%s reset\n", buf);
		return;
	}

	if (a_tcp->nids_state == NIDS_DATA)
	{
		tcp_state = NIDS_DATA;
		// new data has arrived; gotta determine in what direction
		// and if it's urgent or not

		struct half_stream *hlf;

//		if (a_tcp->server.count_new_urg)
//		{
//			// new byte of urgent data has arrived
//			strcat(buf, "(urgent->)");
//			buf[strlen(buf) + 1] = 0;
//			buf[strlen(buf)] = a_tcp->server.urgdata;
//			write(1, buf, strlen(buf));
//			return;
//		}
		// We don't have to check if urgent data to client has arrived,
		// because we haven't increased a_tcp->client.collect_urg variable.
		// So, we have some normal data to take care of.
		if (a_tcp->client.count_new)
		{
			// new data for client
			hlf = &a_tcp->client; // from now on, we will deal with hlf var,
			// which will point to client side of conn
			strcat(buf, "(<-)"); // symbolic direction of data
			tcp_side = 1;
		}
		else
		{
			hlf = &a_tcp->server; // analogical
			strcat(buf, "(->)");
			tcp_side = 0;
		}
		//fprintf(stderr, "%s", buf); // we print the connection parameters
		// (saddr, daddr, sport, dport) accompanied
		// by data flow direction (-> or <-)

		snprintf(tcp_data, hlf->count_new, "%s", hlf->data);
		tcp_data_len = hlf->count_new;

		//write(2, hlf->data, hlf->count_new); // we print the newly arrived data

	}
	return;
}

static int get_next(lua_State *L)
{
	tcp_state = 0;
	tcp_side = -1;
	tcp_data_len = 0;
	if(!nids_next ())
	{
		fprintf(stderr,"%s\n",nids_errbuf);
		lua_pushinteger(L, -1);
		return 1;
	}
	lua_pushinteger(L, tcp_state);
	lua_pushinteger(L, tcp_side);
	lua_pushinteger(L, tcp_data_len);
	lua_pushlstring(L, tcp_data, tcp_data_len);

	return 4;
}

//char *str
static int pcap_init(lua_State *L)
{
	char res[100];
	char *pcap_file = luaL_checkstring(L, 1);
	char *filter = luaL_checkstring(L, 2);
	sprintf(res,"hello %s %s", __FUNCTION__, pcap_file);

	if(is_pcap_file(pcap_file))
	{
		nids_params.filename = pcap_file;
		nids_params.pcap_filter = filter;
		if(!nids_init ())
		{
			fprintf(stderr,"%s\n",nids_errbuf);
			lua_pushinteger(L, -1);
		}
		else
		{
			nids_register_tcp(tcp_callback);
			lua_pushinteger(L, 0);
		}
	}
	else
	{
		lua_pushinteger(L, 1);
	}

	return 1;
}
static int pcap_exit(lua_State *L)
{
	nids_exit();
	return 0;
}

static const struct luaL_Reg load_pcap[] = {
	{"pcap_init", pcap_init},
	{"get_next", get_next},
	{"pcap_exit", pcap_exit},
	{NULL, NULL}
};

int luaopen_libload_pcap (lua_State *L)
{
	luaL_register(L, "load_pcap", load_pcap);
	return 1;
}
